package jf.collections.stream;

import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;

import jf.closure.F1;
import jf.closure.F1B;
import jf.closure.F1V;
import jf.closure.F2;
import jf.collections.ListX;
import jf.collections.Option;
import jf.collections.Tuple2;

/**
 * @author ivanalx
 * @date 03.06.2009 10:24:20
 */
public abstract class Stream<A> implements ListX<A> {
	private F1<Stream<A>, A> builder;

	private ListX<A> baseList = createBaseList();


	public Stream(F1<Stream<A>, A> builder) {
		this.builder = builder;
	}

	public Stream(F1<Stream<A>, A> builder, A... init) {
		this.builder = builder;
		this.add2This(init);
	}

	public Stream(F1<Stream<A>, A> builder, Collection<? extends A> init) {
		this.builder = builder;
		this.add2This(init);
	}

	
	protected abstract ListX<A> createBaseList();

	protected ListX<A> getBaseList() {
		return baseList; 
	}

	public A get(final int index) {
		if (index < size()) {
			return getBaseList().get(index);
		} else if (index > 0) {
			while (size()  < index + 1) {
				this.add(builder.apply(Stream.this));
			}
			return getBaseList().get(index);
		} else {
			throw new IllegalArgumentException("Illegal index access");
		}
	}

	public static <A> Stream<A> stream(F1<Stream<A>, A> builder) {
		return new ArrayStream<A>(builder);
	}

	public static <A> Stream<A> stream(F1<Stream<A>, A> builder, A... init) {
		return new ArrayStream<A>(builder, init);
	}

	public static <A> Stream<A> stream(F1<Stream<A>, A> builder, Collection<? extends A> init) {
		return new ArrayStream<A>(builder, init);
	}

	public static <A> Stream<A> linkedStream(F1<Stream<A>, A> builder) {
		return new LinkedStream<A>(builder);
	}

	public static <A> Stream<A> linkedStream(F1<Stream<A>, A> builder, A... init) {
		return new LinkedStream<A>(builder, init);
	}

	public static <A> Stream<A> linkedStream(F1<Stream<A>, A> builder, Collection<? extends A> init) {
		return new LinkedStream<A>(builder, init);
	}

	@Override
	public <B> ListX<Tuple2<A, B>> zip(F1<A, B> f) {
		return getBaseList().zip(f);
	}

	@Override
	public ListX<A> reverse() {
		return getBaseList().reverse();
	}

	public Iterator<A> rightIterator() {
		return getBaseList().rightIterator();
	}

	public A last() {
		return getBaseList().last();
	}

	public A head() {
		return getBaseList().head();
	}

	public Option<A> find(F1B<A> f) {
		return getBaseList().find(f);
	}

	public <B> B foldLeft(B b, F2<B, A, B> f) {
		return getBaseList().foldLeft(b, f);
	}

	public <B> B foldRight(B b, F2<B, A, B> f) {
		return getBaseList().foldRight(b, f);
	}

	public boolean forall(F1B<A> f) {
		return getBaseList().forall(f);
	}

	public void foreach(F1V<A> f) {
		getBaseList().foreach(f);
	}

	public int hashCode() {
		return getBaseList().hashCode();
	}

	public boolean equals(Object obj) {
		return getBaseList().equals(obj);
	}

	public String toString(String delimeter, String head, String tail) {
		return getBaseList().toString(delimeter, head, tail);
	}

	public String toString() {
		return getBaseList().toString();
	}

	public Iterator<A> leftIterator() {
		return getBaseList().leftIterator();
	}

	public int size() {
		return getBaseList().size();
	}

	public boolean isEmpty() {
		return getBaseList().isEmpty();
	}

	public boolean contains(Object o) {
		return getBaseList().contains(o);
	}

	public Iterator<A> iterator() {
		return getBaseList().iterator();
	}

	public Object[] toArray() {
		return getBaseList().toArray();
	}

	public <T> T[] toArray(T[] a) {
		return getBaseList().toArray(a);
	}

	public boolean add(A a) {
		return getBaseList().add(a);
	}

	public boolean remove(Object o) {
		return getBaseList().remove(o);
	}

	public boolean containsAll(Collection<?> c) {
		return getBaseList().containsAll(c);
	}

	public boolean addAll(Collection<? extends A> c) {
		return getBaseList().addAll(c);
	}

	public boolean removeAll(Collection<?> c) {
		return getBaseList().removeAll(c);
	}

	public boolean retainAll(Collection<?> c) {
		return getBaseList().retainAll(c);
	}

	public void clear() {
		getBaseList().clear();
	}

	public ListX<A> filter(F1B<A> filter) {
		return getBaseList().filter(filter);
	}

	public <B> ListX<B> map(F1<A, B> transformer) {
		return getBaseList().map(transformer);
	}

	public ListX<A> slice(int from, int to) {
		return getBaseList().slice(from, to);
	}

	public ListX<A> drop(int n) {
		return getBaseList().drop(n);
	}

	public ListX<A> dropRight(int n) {
		return getBaseList().dropRight(n);
	}

	public ListX<A> dropWhile(F1B<A> f) {
		return getBaseList().dropWhile(f);
	}

	public <B> ListX<B> flatMap(F1<A, Iterable<B>> f) {
		return getBaseList().flatMap(f);
	}

	public <B> ListX<B> flatten() {
		return getBaseList().flatten();
	}

	public ListX<A> remove(F1B<A> f) {
		return getBaseList().remove(f);
	}

	public ListX<A> removeDuplicates() {
		return getBaseList().removeDuplicates();
	}

	public <S extends A> ListX<S> unsafeWiden() {
		return getBaseList().unsafeWiden();
	}

	public <S> ListX<S> unsafeCastToOtherList() {
		return getBaseList().unsafeCastToOtherList();
	}

	public <C extends ListX<? super A>> C safe() {
		return getBaseList().<C>safe();
	}

	public ListX<A> sort() {
		return getBaseList().sort();
	}

	public ListX<A> sort(Comparator<A> c) {
		return getBaseList().sort(c);
	}

	public ListX<A> tail() {
		return getBaseList().tail();
	}

	public ListX<A> begin() {
		return getBaseList().begin();
	}

	public <B> ListX<Tuple2<A, B>> zip(List<B> that) {
		return getBaseList().zip(that);
	}

	public ListX<Tuple2<A, Integer>> zipWithIndex() {
		return getBaseList().zipWithIndex();
	}

	public ListX<A> takeRight(int n) {
		return getBaseList().takeRight(n);
	}

	public ListX<A> takeLeft(int n) {
		return getBaseList().takeLeft(n);
	}

	public ListX<A> takeWhile(F1B<A> f) {
		return getBaseList().takeWhile(f);
	}

	public Tuple2<ListX<A>, ListX<A>> span(F1B<A> f) {
		return getBaseList().span(f);
	}

	public ListX<A> addX(A a) {
		return getBaseList().addX(a);
	}

	public ListX<A> addX(Collection<? extends A> a) {
		return getBaseList().addX(a);
	}

	public ListX<A> addX(A... a) {
		return getBaseList().addX(a);
	}

	public ListX<A> removeX(A a) {
		return getBaseList().removeX(a);
	}

	public ListX<A> removeX(A... a) {
		return getBaseList().removeX(a);
	}

	public ListX<A> removeX(Collection<? extends A> a) {
		return getBaseList().removeX(a);
	}

	public ListX<A> add2This(A a) {
		return getBaseList().add2This(a);
	}

	public ListX<A> add2This(A... a) {
		return getBaseList().add2This(a);
	}

	public ListX<A> add2This(Collection<? extends A> a) {
		return getBaseList().add2This(a);
	}

	public ListX<A> add2This(int index, A a) {
		return getBaseList().add2This(index, a);
	}

	public ListX<A> add2This(int index, A... a) {
		return getBaseList().add2This(index, a);
	}

	public ListX<A> add2This(int index, Collection<? extends A> a) {
		return getBaseList().add2This(index, a);
	}

	public boolean add(int index, A... col) {
		return getBaseList().add(index, col);
	}

	public ListX<A> removeAtX(Integer... at) {
		return getBaseList().removeAtX(at);
	}

	public ListX<A> removeAtFromThis(Integer... at) {
		return getBaseList().removeAtFromThis(at);
	}

	public A getRight(int index) {
		return getBaseList().getRight(index);
	}

	public ListX<A> removeAtX(Collection<Integer> a) {
		return getBaseList().removeAtX(a);
	}

	public ListX<A> removeAtFromThis(Collection<Integer> a) {
		return getBaseList().removeAtFromThis(a);
	}

	public boolean addAll(int index, Collection<? extends A> c) {
		return getBaseList().addAll(index, c);
	}

	public A set(int index, A element) {
		return getBaseList().set(index, element);
	}

	public void add(int index, A element) {
		getBaseList().add(index, element);
	}

	public A remove(int index) {
		return getBaseList().remove(index);
	}

	public int indexOf(Object o) {
		return getBaseList().indexOf(o);
	}

	public int lastIndexOf(Object o) {
		return getBaseList().lastIndexOf(o);
	}

	public ListIterator<A> listIterator() {
		return getBaseList().listIterator();
	}

	public ListIterator<A> listIterator(int index) {
		return getBaseList().listIterator(index);
	}

	@Override
	public List<A> subList(int fromIndex, int toIndex) {
		return getBaseList().subList(fromIndex, toIndex);
	}

}
